﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MindFusion.Diagramming;
using MindFusion.Diagramming.WinForms;
using CommonLib;
using Constructor.Frames;
namespace Constructor
{
    public partial class FrameConstructor : UserControl
    {
        List<Frame> frames = new List<Frame>();
        public event EventHandler<EventArgsZoom> Zoomed;
        public FrameConstructor()
        {
            InitializeComponent();
            this.diagramView.Diagram.NodeCreated += new NodeEventHandler(Diagram_NodeCreated);
            this.diagramView.Diagram.NodeClicked += new NodeEventHandler(Diagram_NodeClicked);
            this.diagramView.Diagram.NodeDeleted += new NodeEventHandler(Diagram_NodeDeleted);
            this.diagramView.Diagram.NodeCreating += new NodeValidationEventHandler(Diagram_NodeCreating);
            this.diagramView.Diagram.NodeDeleting += new NodeValidationEventHandler(Diagram_NodeDeleting);
            this.diagramView.Diagram.NodeDoubleClicked += new NodeEventHandler(Diagram_NodeDoubleClicked);
            diagramView.MouseWheel += new MouseEventHandler(diagramView_MouseWheel);
            External.frames = frames;
            External.explorer = objectExplorer1;
        }

        void Diagram_NodeDoubleClicked(object sender, NodeEventArgs e)
        {
            new EditorSlot((diagram1.ActiveItem as TableNode).Tag as Frame) { TopMost = FindForm().TopMost }.ShowDialog();
        }

        void Diagram_NodeDeleting(object sender, NodeValidationEventArgs e)
        {
            if ((e.Node.Tag as Frame).Name == "Поле" || (e.Node.Tag as Frame).Name == "Клетка" || (e.Node.Tag as Frame).Name == "Объект")
            {
                MessageBox.Show("Нельзя удалить этот фрейм");
                e.Cancel = true;
                return;
            }
            foreach (Frame f in External.frames)
            {
                if (f.ParentFrame != null && (e.Node.Tag as Frame).Name == f.ParentFrame.Name)
                {
                    MessageBox.Show("У этого фрейма есть потомки. Его нельзя удалить");
                    e.Cancel = true;
                    return;
                }
            }
            if (MessageBox.Show("Действительно удалить фрейм " + (e.Node.Tag as Frame).Name + "?", "", MessageBoxButtons.YesNo) == DialogResult.No)
                e.Cancel = true;
        }
        void diagramView_DestroyEditControl(object sender, MindFusion.Diagramming.WinForms.InPlaceEditEventArgs e)
        {
            //if (_col != -1 && _row != -1 && _table != null)
            //{
            //    _table[_col, _row].Text =
            //        cmb.Items[cmb.SelectedIndex].ToString();
            //}
            //cmb.Visible = false;
            //switch (cmb.Text)
            //{
            //    case "Прототип":
            //        (e.Node.Tag as Frame).Type = CommonLib.FrameType.Prototype;
            //        break;
            //    case "Экземпляр":
            //        (e.Node.Tag as Frame).Type = CommonLib.FrameType.Exemplar;
            //        break;
            //    case "Ситуация":
            //        (e.Node.Tag as Frame).Type = CommonLib.FrameType.Situation;
            //        break;
            //}
        }
        TableNode _table;
        int _col, _row;
        void diagramView_CreateEditControl(object sender, MindFusion.Diagramming.WinForms.InPlaceEditEventArgs e)
        {
            //TableNode.Cell cell = e.Item as TableNode.Cell;            
            //if (cell != null)
            //{
            //    RectangleF docRect = e.Item.GetEditRect(PointF.Empty);
            //    cmb.Bounds = diagramView.DocToClient(docRect);
            //    cmb.Visible = true;
            //    e.EditControl = cmb;

            //    for (int i = 0; i < cmb.Items.Count; i++)
            //    {
            //        if (cmb.Items[i].ToString() == cell.Text)
            //        {
            //            cmb.SelectedIndex = i;
            //            return;
            //        }
            //    }
            //    switch (Properties.Settings.Default.defaultFrameType)
            //    {
            //        case CommonLib.FrameType.Exemplar:
            //            cmb.SelectedIndex = 1;
            //            break;
            //        case CommonLib.FrameType.Prototype:
            //            cmb.SelectedIndex = 0;
            //            break;
            //        case CommonLib.FrameType.Situation:
            //            cmb.SelectedIndex = 2;
            //            break;
            //    }

            //}
        }

        void Diagram_CellClicked(object sender, CellEventArgs e)
        {
            //            cmb.SelectedIndex = 0;
            _table = e.Table;
            _row = e.Row;
            _col = e.Column;
            diagramView.BeginEdit(e.Cell);
        }

        void Diagram_NodeCreating(object sender, NodeValidationEventArgs e)
        {
            (e.Node as TableNode).RowCount = 0;
            (e.Node as TableNode).ColumnCount = 2;
            (e.Node as TableNode).Caption = "";
        }

        void Diagram_NodeDeleted(object sender, NodeEventArgs e)
        {
            Frame f = e.Node.Tag as Frame;
            if (f != null)
            {
                DeleteFrame(f);
                try
                {
                    f.TreeNode.Parent.Nodes.Remove(f.TreeNode);
                }
                catch { }
            }

        }
        void Diagram_NodeCreated(object sender, NodeEventArgs e)
        {
            Frame frame = new Frame(GetFrameName());
            frame.Node = e.Node as TableNode;
            frames.Add(frame);
            e.Node.Tag = frame;
            frame.RepaintNode(true);
            e.Node.Selected = true;
            propertiesPanel.Init(e.Node.Tag as Frame);
        }

        void Diagram_NodeClicked(object sender, NodeEventArgs e)
        {
            propertiesPanel.Init(e.Node.Tag as Frame);
            External.explorer.SelectNode((e.Node.Tag as Frame).TreeNode);
            diagram1.ActiveItem = e.Node;
            if (e.MouseButton == MouseButton.Right)
            {
                contextMenuStrip1.Show(this, PointToClient(MousePosition));
            }
        }
        public string GetFrameName()
        {
            int i = 0;
            while (true)
            {
                if (External.SearchFrame("Frame" + i.ToString()) == null) break;
                i++;
            }
            return "Frame" + i.ToString();
        }
        public DataTable SaveNodes()
        {
            DataTable table = new DataTable("nodes");
            table.Columns.Add("frame");
            table.Columns.Add("width");
            table.Columns.Add("height");
            table.Columns.Add("x");
            table.Columns.Add("y");
            foreach (TableNode tn in diagram1.Nodes)
            {
                DataRow dr = table.NewRow();
                dr["frame"] = (tn.Tag as Frame).Name;
                dr["width"] = tn.GetRepaintRect(false).Width - 3;
                dr["height"] = tn.GetRepaintRect(false).Height - 3;
                dr["x"] = tn.GetBounds().X;
                dr["y"] = tn.GetBounds().Y;
                table.Rows.Add(dr);
            }
            return table;
        }

        public void Load(DataSet ds)
        {
            External.frames.Clear();
            diagram1.Nodes.Clear();
            External.Parents.Clear();
            MLO.MLOSource.Exemplars.Clear();
            DataTable table = ds.Tables["Frames"];
            if (table != null)
            {
                foreach (DataRow dr in table.Rows)
                {
                    Frame f = new Frame(dr["name"].ToString());
                    string id_frame = string.Empty;
                    try
                    {
                        id_frame = dr["id"].ToString();
                    }
                    catch
                    {
                        id_frame = Guid.NewGuid().ToString();
                    }
                    finally
                    {
                        f.ID = id_frame;
                    }
                    switch (dr["type"].ToString())
                    {
                        case "Exemplar":
                            f.Type = FrameType.Exemplar;
                            External.Parents.Add(f.ID, External.SearchFrame(dr["prototype"].ToString()));
                            MLO.MLOSource.Exemplars.Add(f);
                            break;
                        case "Prototype":
                            f.Type = FrameType.Prototype;
                            External.frames.Add(f);
                            break;
                        case "Situation":
                            f.Type = FrameType.Situation;
                            External.frames.Add(f);
                            break;
                    }
                    int temp = 0;
                    try
                    {
                        temp = int.Parse(dr["priority"].ToString());
                    }
                    catch { }
                    finally { f.Priority = temp; }
                    
                    
                }
            }
            table = ds.Tables["Slots"];
            if (table != null)
            {
                foreach (DataRow dr in table.Rows)
                {
                    Frame f = External.SearchFrame(dr["id_frame"].ToString()) ?? External.SearchFrameIn(MLO.MLOSource.Exemplars, dr["id_frame"].ToString());
                    if (f != null)
                    {
                        Slot s = new Slot(f);
                        f.Slots.Add(s);
                        s.Name = dr["name"].ToString();
                        string id_slot = string.Empty;
                        try
                        {
                            id_slot = dr["id"].ToString();
                        }
                        catch
                        {
                            id_slot = Guid.NewGuid().ToString();
                        }
                        finally
                        {
                            s.ID = id_slot;
                        }
                        switch (dr["type"].ToString())
                        {
                            case "Regular":
                                s.Type = SlotType.Regular;
                                s.DomainID = dr["id_domain"].ToString();
                                s.Default = dr["default"].ToString();
                                switch (dr["inherit"].ToString())
                                {
                                    case "Range":
                                        s.Inherit = TypeInherit.Range;
                                        break;
                                    case "Overwrite":
                                        s.Inherit = TypeInherit.Overwrite;
                                        break;
                                    case "Same":
                                        s.Inherit = TypeInherit.Same;
                                        break;
                                }
                                break;
                            case "SubFrame":
                                s.Subframe = External.SearchFrame(dr["subframe"].ToString()) ?? External.SearchFrameIn(MLO.MLOSource.Exemplars, dr["subframe"].ToString());
                                bool temp = false;
                                try
                                {
                                    bool.TryParse(dr["canbenull"].ToString(), out temp);
                                }
                                catch { }
                                finally { s.CanBeNull = temp; }
                                s.Type = SlotType.SubFrame;
                                break;
                            case "AKO":
                                s.Subframe = External.SearchFrame(dr["subframe"].ToString()) ?? External.SearchFrameIn(MLO.MLOSource.Exemplars, dr["subframe"].ToString());
                                s.Type = SlotType.AKO;

                                break;
                            case "Error":
                                s.Subframe = External.SearchFrame(dr["subframe"].ToString()) ?? External.SearchFrameIn(MLO.MLOSource.Exemplars, dr["subframe"].ToString());
                                s.Type = SlotType.Error;

                                break;
                            case "Next":
                                s.Subframe = External.SearchFrame(dr["subframe"].ToString()) ?? External.SearchFrameIn(MLO.MLOSource.Exemplars, dr["subframe"].ToString());
                                s.Type = SlotType.Next;

                                break;
                            case "Production":
                                s.Type = SlotType.Production;
                                DataRow[] productions = ds.Tables["Productions"].Select("owner_frame = '" + f.ID + "'");
                                DataRow product = null;
                                foreach (DataRow row in productions)
                                    if (row["owner_slot"].ToString() == s.Name)
                                    {
                                        product = row;
                                        break;
                                    }
                                Production p = new Production(s);
                                s.Production = p;
                                break;
                        }
                    }
                }
            }
            table = ds.Tables["Productions"];
            if (table != null)
            {
                foreach (DataRow product in table.Rows)
                {
                    Production p = External.SearchFrame(product["owner_frame"].ToString()).SearchInAllSlot(product["owner_slot"].ToString()).Production;
                    p.Item = External.GetItem(product["slot"].ToString());
                    p.Operation = product["operation"].ToString();
                    bool b = false;
                    bool.TryParse(product["marker"].ToString(), out b);
                    p.Marker = b;
                    b = false;
                    try
                    {
                        bool.TryParse(product["procedure"].ToString(), out b);
                    }
                    catch { }
                    finally
                    {
                        p.isProc = b;
                    }
                    switch (product["type"].ToString())
                    {
                        case "Enum":
                            p.Type = CommonLib.ValueType.Enum;
                            p.Value = new NewItem(product["value"].ToString(),null);
                            break;
                        case "Frame":
                            p.Type = CommonLib.ValueType.Frame;
                            p.Value = External.GetItem(product["value"].ToString());
                            break;
                        case "Int":
                            p.Type = CommonLib.ValueType.Int;
                            if (!p.isProc)
                                p.Value = new NewItem(product["value"].ToString(),null);
                            else
                            {
                                p.Procedure = new Procedure(p);
                                p.Procedure.Load(ds);
                            }
                            break;
                        case "IntVar":
                            p.Type = CommonLib.ValueType.IntVar;
                            if (!p.isProc)
                                p.Value = new NewItem(product["value"].ToString(),null);
                            else
                            {
                                p.Procedure = new Procedure(p);
                                p.Procedure.Load(ds);
                            }
                            break;
                    }
                }
            }
            table = ds.Tables["nodes"];
            if (table != null)
            {
                foreach (DataRow dr in table.Rows)
                {
                    TableNode node = new TableNode(diagram1);
                    node.SetRect(new RectangleF(float.Parse(dr["x"].ToString()), float.Parse(dr["y"].ToString())
                        , float.Parse(dr["width"].ToString()), float.Parse(dr["height"].ToString())), false);
                    diagram1.Nodes.Add(node);
                    Frame f = External.SearchFrame(dr["frame"].ToString());
                    node.Tag = f;
                    f.Node = node;
                    f.RepaintNode(false);
                    node.ResizeToFitText(false);
                }
            }
            AddField();
        }
        private void AddStandartDomain()
        {
            DataTable dt = External.ds.Tables["domens"];
            if (dt != null && !Domain.Exists("Координаты"))
            {
                DataRow dr = dt.NewRow();
                dr["Название_домена"] = "Координаты";
                dt.Rows.Add(dr);
                External.ds.Tables["domens_params"].Rows.Add(new object[] { "Координаты", "0..100" });
            }
            if (dt != null && !Domain.Exists("Логический"))
            {
                DataRow dr = dt.NewRow();
                dr["Название_домена"] = "Логический";
                dt.Rows.Add(dr);
                External.ds.Tables["domens_params"].Rows.Add(new object[] { "Логический", "Да" });
                External.ds.Tables["domens_params"].Rows.Add(new object[] { "Логический", "Нет" });
            }
        }
        public void SetRankField(int rank)
        {
            Frame field = External.SearchFrame("Поле");
            if (field != null)
            {
                field.Slots.Clear();
                for (int i = 0; i < rank * rank; i++)
                {
                    Slot s = new Slot(field);
                    field.Slots.Add(s);
                    s.Type = SlotType.SubFrame;
                    s.Subframe = External.SearchFrame("Клетка");
                    s.Name = "Клетка" + i.ToString();
                    s.Default = "Клетка";
                }
                field.RepaintNode(false);
                field.Node.ResizeToFitText(false);
            }
        }
        public void AddField()
        {
            AddStandartDomain();            
            //var cell1 = External.SearchFrame("Клетка");
            //if ((cell1 != null))
            //{
            //    Slot z1 = new Slot(cell1);
            //    cell1.Slots.Add(z1);
            //    z1.Name = "Занятость";
            //    z1.Type = SlotType.Regular;
            //    z1.DomainID = "Логический";
            //    z1.Inherit = TypeInherit.Overwrite;
            //    z1.Default = "Нет";
            //}
            if (External.SearchFrame("Клетка") == null)
            {
                Frame cell = new Frame("Клетка");
                cell.Type = FrameType.Prototype;
                Slot x = new Slot(cell);
                cell.Slots.Add(x);
                x.Name = "X";
                x.Type = SlotType.Regular;
                x.DomainID = "Координаты";
                x.Inherit = TypeInherit.Overwrite;
                if (x.Domain.Type == DomainType.Number)
                    x.Default = x.Domain.MinValue;
                Slot y = new Slot(cell);
                cell.Slots.Add(y);
                y.Name = "Y";
                y.Type = SlotType.Regular;
                y.DomainID = "Координаты";
                y.Inherit = TypeInherit.Overwrite;
                if (y.Domain.Type == DomainType.Number)
                    y.Default = y.Domain.MinValue;
                External.frames.Add(cell);
                Slot z = new Slot(cell);
                cell.Slots.Add(z);
                z.Name = "Занятость";
                z.Type = SlotType.Regular;
                z.DomainID = "Логический";
                z.Inherit = TypeInherit.Overwrite;
                z.Default = "Нет";
                TableNode node = new TableNode(diagram1);
                node.SetRect(new RectangleF(1, 21, 10, 10), false);
                node.Tag = cell;
                cell.Node = node;
                cell.RepaintNode(false);
                node.ResizeToFitText(false);
                diagram1.Nodes.Add(node);
            }
            if (External.SearchFrame("Поле") == null)
            {
                Frame field = new Frame("Поле");
                TableNode node = new TableNode(diagram1);
                node.SetRect(new RectangleF(1, 1, 20, 20), false);
                node.Tag = field;
                field.Node = node;
                diagram1.Nodes.Add(node);

                External.frames.Add(field);
                field.Type = FrameType.Prototype;
                SetRankField(10);
            }
            if (External.SearchFrame("Объект") == null)
            {
                Frame obj = new Frame("Объект");
                TableNode node = new TableNode(diagram1);
                node.SetRect(new RectangleF(1, 1, 20, 20), false);
                node.Tag = obj;
                obj.Node = node;
                diagram1.Nodes.Add(node);

                External.frames.Add(obj);
                obj.Type = FrameType.Prototype;
                Slot s = new Slot(obj);
                s.Name = "Позиция";
                s.Type = SlotType.SubFrame;
                s.Subframe = External.SearchFrame("Клетка");
                obj.Slots.Add(s);

                s = new Slot(obj);
                s.Name = "Иконка";
                s.Type = SlotType.Regular;
                s.DomainID = "Images";
                obj.Slots.Add(s);
                obj.RepaintNode(false);
                node.ResizeToFitText(true);
            }
            External.explorer.Fill();
        }
        public void DeleteFrame(Frame f)
        {
            TableNode node = null;
            foreach (TableNode tn in diagram1.Nodes)
                if (tn.Tag as Frame == f) node = tn;
            diagram1.Nodes.Remove(node);
            External.frames.Remove(f);
            propertiesPanel.Hide();
            //External.explorer.Fill();
        }
        public void SelectNode(TreeNode node)
        {
            if (node != null && (node.Tag as Frame != null || node.Tag as Slot != null))
            {
                Frame f = node.Tag as Frame ?? (node.Tag as Slot).Frame;
                propertiesPanel.Init(f);
                diagramView.BringIntoView(f.Node);
                diagram1.ActiveItem = f.Node;
            }
        }
        private bool ExistLink(TableNode node1, TableNode node2)
        {
            foreach (DiagramLink link in diagram1.Links)
                if (link.Origin == node1 && link.Destination == node2 && link.Text == "sub") return true;
            return false;
        }
        public void DrawLinks()
        {
            diagram1.Links.Clear();
            diagram1.LinkStyle = LinkStyle.Polyline;
            if (Properties.Settings.Default.showAKO)
                foreach (Frame f in External.frames)
                {
                    if (f.ParentFrame != null && f.Node != null & f.ParentFrame.Node != null)
                    {
                        DiagramLink link = new DiagramLink(diagram1, f.Node, f.ParentFrame.Node);
                        link.Text = "is a";
                        diagram1.Links.Add(link);
                    }
                }
            if (Properties.Settings.Default.showSubFrame)
                foreach (Frame f in External.frames)
                {
                    foreach (Slot slot in f.GetSlots(SlotType.SubFrame, false))
                    {
                        if (!ExistLink(f.Node, slot.Subframe.Node) && f.Node != null && slot.Subframe.Node != null)
                        {
                            DiagramLink link = new DiagramLink(diagram1, f.Node, slot.Subframe.Node);
                            //link.ShadowBrush = new MindFusion.Drawing.SolidBrush(Color.Green);
                            link.Text = "sub";
                            diagram1.Links.Add(link);
                        }
                    }
                }
            diagramView.Refresh();
        }
        private void редактироватьСлотыToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new EditorSlot((diagram1.ActiveItem as TableNode).Tag as Frame) { TopMost = FindForm().TopMost }.ShowDialog();
        }

        private void сохранитьToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }

        private void загрузитьToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }

        private void подогнатьРазмерToolStripMenuItem_Click(object sender, EventArgs e)
        {
            (diagram1.ActiveItem as TableNode).ResizeToFitText(true);
        }

        private void показатьВесьToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TableNode node = diagram1.ActiveItem as TableNode;
            node.SetBounds(new RectangleF(1, 1, Math.Min(node.GetBounds().Width, 30), Math.Min(node.GetBounds().Height, 30)), false, false);
            diagramView.Refresh();
        }
        private void diagram1_NodeSelected(object sender, NodeEventArgs e)
        {

        }

        #region Zoom&Scroll
        public void Zoom(float coef)
        {
            if (coef != -1)
            {
                diagramView.ZoomFactor = coef;
                diagramView.ZoomIn();
            }
            else
                diagramView.ZoomToFit();
        }


        public class EventArgsZoom : EventArgs
        {
            public EventArgsZoom(float zoom)
            {
                Zoom = zoom;
            }
            public float Zoom { get; set; }
        }
        protected virtual void OnZoom(EventArgsZoom e)
        {
            if (Zoomed != null)
                Zoomed(diagramView, e);
        }
        void diagramView_MouseWheel(object sender, MouseEventArgs e)
        {
            if (ModifierKeys == Keys.Control)
            {
                diagramView.ZoomFactor += e.Delta / 4;
                diagramView.ZoomIn();
                OnZoom(new EventArgsZoom(diagramView.ZoomFactor));
                return;
            }
            int shift = 30;
            if (e.Delta < 0)
                diagramView.ScrollTo(0, diagramView.ScrollY + shift);
            else
            {
                diagramView.ScrollTo(0, Math.Max(diagramView.ScrollY - shift, 0));
            }
        }
        #endregion

        private void diagramView_MouseDoubleClick(object sender, MouseEventArgs e)
        {

        }
        private Frame CreateFrame(float x, float y)
        {
            TableNode node = new TableNode(diagram1);
            Frame frame = new Frame(GetFrameName());
            frame.Node = node;
            frames.Add(frame);
            node.Tag = frame;
            frame.RepaintNode(true);
            node.Selected = true;
            node.SetRect(new RectangleF(x,y, 50, 20), false);
            //надо у диаграмм а не у диаграмм вью            
            diagram1.Nodes.Add(node);
            node.Move(x, y);
            //External.explorer.Fill();
            External.explorer.AddFrame(frame);
            propertiesPanel.Init(frame);
            diagram1.ActiveItem = node;
            return frame;
        }
        private void diagram1_DoubleClicked(object sender, DiagramEventArgs e)
        {
            CreateFrame(e.MousePosition.X, e.MousePosition.Y);
        }

        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            редактироватьСлотыToolStripMenuItem.Enabled = (diagram1.ActiveItem.Tag as Frame).Name != "Поле";
        }

        private void удалитьToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Frame f = diagram1.ActiveItem.Tag as Frame;
            if (f != null)
            {
                if (f.Name == "Поле" || f.Name == "Клетка" || f.Name == "Объект")
                {
                    MessageBox.Show("Нельзя удалить этот фрейм");
                    return;
                }
                foreach (Frame frame in External.frames)
                    if (frame.ParentFrame != null && f.Name == frame.ParentFrame.Name)
                    {
                        MessageBox.Show("У этого фрейма есть потомки. Его нельзя удалить");
                        return;
                    }
                if (MessageBox.Show("Действительно удалить фрейм " + f.Name + "?", "", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    DeleteFrame(f);
                    try
                    {
                        f.TreeNode.Parent.Nodes.Remove(f.TreeNode);
                    }
                    catch { }
                }
            }
        }

        private void createChildToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var item = diagram1.ActiveItem as TableNode;
            diagram1.Selection.Items.Clear();
            float x = item.GetBounds().X;
            float y = item.GetBounds().Y;
            x += 30;
            y += 30;
            var parent = item.Tag as Frame;
            var frame = CreateFrame(x, y);
            Slot s = new Slot(frame);
            s.Type = SlotType.AKO;
            s.Name = "AKO";
            s.Subframe = parent;
            frame.Slots.Add(s);
            frame.RepaintNode(true);
        }

    }
}
